﻿/**
 * Reboulder - Partial Boulder Dash clone
 * Jesús Sagra, 2014
 * 
 * Map class: map for a level
 * 
 * Version    Date     Changes
 * -----------------------------------------------------
 *  0.01   07-dic-2014  Attributes, constructor, draw map, check collisions.
 *  0.02   08-dic-2014  Map reader
 *  0.03   26-feb-2015  Map loader from files
 *  0.04   02-mar-2015  CanMoveTo , Gravities, CanRoll
 *                      CanPushTo , PushRight, PushLeft
 */

using System.IO;
using System.Collections.Generic;

class Map
{
    protected int mapWidth;
    protected int mapHeight;
    protected int backgroundTileSize;
    protected int amountOfItems;
    protected string[] level;     //Auxiliar array to read the map from it

    protected List<Sprite> sprites;
    protected Door mapDoor;
    protected Player player1;
    
    public Map(string levelName)
    {
        amountOfItems = 0;
        backgroundTileSize = 32;
        LoadMap(levelName);
        ReadMap();
    }


    // Read map from file. 
    public void LoadMap(string file)
    {
        StreamReader mapReader = File.OpenText(file + ".txt");
        mapWidth = System.Convert.ToInt32(mapReader.ReadLine());
        mapHeight = System.Convert.ToInt32(mapReader.ReadLine());
        level = new string[mapHeight];
        for (int i = 0; i < mapHeight; i++)
        {
            level[i] = mapReader.ReadLine();
        }
    }


    public void ReadMap()
    {
        sprites = new List<Sprite>();
        
        for (int row = 0; row < mapHeight; row++)
        {
            for (int col = 0; col < mapWidth; col++)
            {
                switch (level[row][col])
                {
                    case '@':
                        mapDoor = new Door(col * backgroundTileSize,
                            row * backgroundTileSize + 32);
                        break;
                    case 'O':
                        sprites.Add(new Rock(col * backgroundTileSize,
                            row * backgroundTileSize + 32));
                        break;
                    case '_':
                        sprites.Add(new Sand(col * backgroundTileSize,
                            row * backgroundTileSize + 32));
                        break;
                    case 'X':
                        sprites.Add(new Item(col * backgroundTileSize,
                            row * backgroundTileSize + 32));
                        amountOfItems++;
                        break;
                    case 'P':
                        player1 = new Player(col * backgroundTileSize,
                            row * backgroundTileSize + 32);
                        break;
                    case 'B':
                        sprites.Add(new Brick(col * backgroundTileSize,
                            row * backgroundTileSize + 32));
                        break;
                    case '#':
                        sprites.Add(new Wall(col * backgroundTileSize,
                            row * backgroundTileSize + 32));
                        break;
                }
            }
        }
    }


    public int GetAmountOfItems()
    {
        return amountOfItems;
    }


    public Player GetPlayer()
    {
        return player1;
    }


    public Door GetDoor()
    {
        return mapDoor;
    }


    public void Draw()
    {
        foreach (Sprite currentSprite in sprites)
        {
            currentSprite.DrawOnHiddenScreen();
        }
        mapDoor.DrawOnHiddenScreen();/////////////////////////////////////////////
    }


    public bool CanMoveTo(int xStart, int yStart, int xEnd, int yEnd)
    {
        // Check if player collides with any element that he can't cross
        foreach (Sprite currentSprite in sprites)
        {
            if (currentSprite.CollisionsWith(xStart, yStart, xEnd, yEnd) &&
                !currentSprite.Crossable)
            {
                return false;
            }
        }
        // If player doesn't collide with anyone, can move to that position
        return true;
    }


    public void MoveRoundObjects()
    {
        foreach (Sprite currentSprite in sprites)
        {
            if (currentSprite.Round &&
                CanFallDown(currentSprite.X, currentSprite.Y))
            {
                currentSprite.Falling = true;
                currentSprite.MoveDown();
            }
            else if (currentSprite.Round &&
                CanFallDownLeft(currentSprite.X, currentSprite.Y))
            {
                currentSprite.Falling = true;
                currentSprite.MoveLeft();
                currentSprite.MoveDown();
            }
            else if (currentSprite.Round &&
                CanFallDownRight(currentSprite.X, currentSprite.Y))
            {
                currentSprite.Falling = true;
                currentSprite.MoveRight();
                currentSprite.MoveDown();
            }
        }
    }


    public string GetCollisions()
    {
        foreach(Sprite currentSprite in sprites)
        {
            if (player1.CollisionsWith(currentSprite) && currentSprite.Visible)
            {
                string spriteType = currentSprite.ToString();
                switch (spriteType)
                {
                    case "Item":
                        currentSprite.Visible = false;
                        return spriteType;
                    case "Sand":
                        currentSprite.Visible = false;
                        break;
                    case "Rock"://///////////////////////////////////
                        if (!currentSprite.Falling)
                            return spriteType;
                        break;                     
                }
            }
        }
        return "";
    }


    // Check if sprites can fall down
    // Parameter: 
    // Return: bool, true if rock can fall
    public bool CanFallDown(int x, int y)
    {
        foreach (Sprite currentSprite in sprites)
	    {
            if (CheckDownTile(x,y))
            {
                return false;
            }
	    }
        return true;
    }


    //Check if sprites can fall down-left
    //Parameter:  
    //Return: bool, true if rock can fall
    public bool CanFallDownLeft(int x , int y)
    {
        foreach (Sprite currentSprite in sprites)
	    {
            if (CheckLeftTile(x , y) || CheckDownLeftTile(x , y))
            {
                return false;
            }
	    }
        return true;
    }


    // Check if sprites can fall down-right
    // Parameter: 
    // Return: bool, true if rock can fall
    public bool CanFallDownRight(int x, int y)
    {
        foreach (Sprite currentSprite in sprites)
        {
            if (CheckRightTile(x, y) || CheckDownRightTile(x, y))
            {
                return false;
            }
        }
        return true;
    }


    public bool CheckLeftTile(int x , int y)
    {
        foreach (Sprite currentSprite in sprites)
        {
            if ((x - currentSprite.Width == currentSprite.X) && 
                (y == currentSprite.Y) &&
                currentSprite.Visible)
                return true;
        }
        return false;
    }


    public bool CheckDownLeftTile(int x , int y)
    {
        foreach (Sprite currentSprite in sprites)
        {
            if ((x - currentSprite.Width == currentSprite.X) &&
                (y + currentSprite.Height == currentSprite.Y) && 
                currentSprite.Visible)
                return true;
        }
        return false;
    }

    
    public bool CheckRightTile(int x , int y)
    {
        foreach (Sprite currentSprite in sprites)
        {
            if ((x + currentSprite.Width == currentSprite.X) &&
                (y == currentSprite.Y) &&
                currentSprite.Visible)
                return true;
        }
        return false;
    }


    //If something visible below return true;
    public bool CheckDownTile(int x , int y)
    {
        foreach (Sprite currentSprite in sprites)
	    {
            if ((x == player1.X) &&
                (y == player1.Y - player1.Height))
                return true; 
            
            if ((x == currentSprite.X) &&
                (y == currentSprite.Y - currentSprite.Height) &&
                currentSprite.Visible)
                return true;
	    }
        return false;
    }


    public bool CheckDownRightTile(int x , int y)
    {
        foreach (Sprite currentSprite in sprites)
        {
            if ((x + currentSprite.Width == currentSprite.X) &&
                (y + currentSprite.Height == currentSprite.Y) &&
                currentSprite.Visible)
                return true;
        }
        return false;
    }
}